package communication.thirdLayer;
import communication.*;
import java.util.Vector;
import java.util.LinkedList;
import java.util.HashMap;
import objects.Station;
import communication.pack.*;
import kernel.Simulator;
import java.io.Serializable;
import utils.*;


/**
 * @author gerard
 *
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
public class ThirdLayerManager implements Serializable {
	
    /**
     * the CommunicationManager who owes the ThirdLayerManager 
     */
    private CommunicationManager comManager;
    /**
     * the protocol of third layer that this ThirdLayerManager uses
     */
    private ThirdLayerProtocol myProtocol;
    /**
     * the object currentPackages which stocks all the packages which are in 
     * communication currently (which is being sent or received)
     */
    private CurrentPackages currentPackages;
    /**
     * this fileIDcounter is used to identify the files sent in the network
     */
    private static int fileIDcounter=0;
	
    /**
     * constructor from a CommunicationManager and a protocol of third layer
     * @param cm the CommunicationManager whose owes the ThirdLayerManager
     * @param tLProtocol: the ThirdLayerProtocol used in the simulation
     */
    public ThirdLayerManager(CommunicationManager cm,ThirdLayerProtocol tLProtocol){
	comManager = cm;
	myProtocol = tLProtocol;
	currentPackages= new CurrentPackages(this);
    }
	
    /**
     * 
     * @return the currentPackages
     */
    public CurrentPackages getCurrentPackages(){
	return this.currentPackages;
    }

    /**
     * @return the CommunicationManager
     */
    public CommunicationManager getComManager() {
	return comManager;
    }
	
    /**
     * 
     * @return the protocol of third layer
     */
    public ThirdLayerProtocol getTLProtocol(){
	return this.myProtocol;
    }

    public void setTLProtocol(ThirdLayerProtocol tl) {
	myProtocol=tl;
    }
    
    /**
     * 
     * @param datasize
     * @return a Vector containing sizes of all the packages fragmented from an initial sum
     * of data (without heading) 
     */
    //Ajoute par Tien 18/05/05
    public Vector fragmentation(int datasize){
	Vector sizes= new Vector(); 
	int s;
	if(this.getTLProtocol() instanceof AODVProtocol) 
	    s=AODVProtocol.DataPackageMaxsize;
	else s=OLSRProtocol.DataPackageMaxsize;
			
	if(datasize<=s)
	    sizes.add(new Integer(datasize));
	else{
	    int a=datasize/s;  //a: le nombre de paquets
	    int b=datasize-a*s; //b: la taille du dernier paquet
	    for(int i=0; i<a;i++){
		sizes.add(new Integer(s));
	    }
	    if(b!=0) sizes.add(new Integer(b));
	}
		
	return sizes;
    }
	
    /**
     * We consider that this method is called directly from IHM when the user 
     * wants to send an amount of data from the station source to the station 
     * destination 
     * @param size, the size of the package that the user wants to send. 
     * @param source
     * @param destination
     */
    public void sendPackage(int size, Station source, Station destination){
	Vector packages=fragmentation(size);
	//t: nombre de paquets envoyes
	int t=packages.size();
	int fileId=fileIDcounter;
	int tempsExces=0;
	fileIDcounter++;
	if(t<=0){
	    System.out.println("Please send something!\n");
	}
	else{
	    if(t>=1){
		for(int i=0; i<t; i++){
		    DataPackage dp=new DataPackage(source, destination,
						   source.getRouter().getNoSequence(),
						   0, source.getRouter().getNoRequest(),
						   ((Integer)packages.elementAt(i)).intValue(),
						   fileId,
						   source.getRouter().getTTL(),size);
		    source.getRouter().send(dp);
		}
	    }
	}
    }
    /**
     * update communication of third layer in the network.
     * This method is called in updateCommunication which is called 
     * in Simulator.calculate() which calculates the state of all the network
     * at each loop of timer  
     * @param wes the vector of all the wishedEmissions that are finished
     */
    public void updateCommunication3(Vector wes){
	//		------THIRD LAYER--------------------
	//		c'est ici qu'on va mettre les fonctions de communication du niveau3:


	//	System.out.println("ThirdLayerManager -> updateCommunication3");

	double helloTime=(this.getComManager().getSimulator().getTime()) % 
	    this.getTLProtocol().getHelloTime();

							
	//1.Envoyer les HelloAODV
	if(helloTime==1) this.getTLProtocol().routesMaintenance();
	//1,5.Incrementer timer de tous les RoutingTableEntry de toutes les stations
	//Purger les entrees inutilisees depuis un moment. 
	//Purger les voisins qui ne disent pas hello (enfin, c'est malpoli!!!)
	if(this.getProtocoleName()=="AODV"){
	    for (int i=0; i<this.getTLProtocol().getNetwork().size();i++){
		RouterAODV r=(RouterAODV)this.getTLProtocol().getNetwork().get(i);
								
		r.timerIncrement();
			
	    }
	    ((AODVProtocol)this.getTLProtocol()).timingPurge();
	}
		
	//2.Recevoir des paquets arrives -> methode dans CurrentPackages
	if(wes.size()>0){
	    for(int i=0; i<wes.size(); i++){
		this.getCurrentPackages().receivedPackageArrival((WishedEmission)wes.get(i));
	    }
	    //3.Traiter des paquets

	    this.getTLProtocol().packagesTreatment();
	}

	//------FIN de THIRD LAYER----------
    }
	
		
    /**
     * This function treats a wishedEmission3 from the vector wishedEmission3 
     * (CommunicationManager) on its start time.
     * It will create all the necessary wishedEmission of second layer   
     *
     */
    public void wishedEmission3Treatment(){
	//System.out.println("wishedEmission3Treatment");
	LinkedList ll=this.getComManager().getWishedEmission3();
		
	if(ll.size()>0){
	    boolean encore=true;
	    WishedEmission3 we3 =((WishedEmission3)ll.getFirst());
	    while((encore)&&
		  (we3.getStartTime()<=this.getComManager().getSimulator().getTime()))
		{
		    Vector receivers=we3.getReceivers();
		    for(int j=0; j<receivers.size();j++){
			this.sendPackage(Integer.decode((String) we3.getParameters().get(0)).intValue(),
					 we3.getSender(),
					 (Station)receivers.get(j));
		    }
		    ll.remove(we3);
		    Station transmitter=we3.getSender();
		    transmitter.removeWishedEmission3(we3);
		    if(ll.size()>0){ 
			we3=(WishedEmission3)ll.getFirst();
			encore=true;
		    }
		    else encore=false;
				
		}
	}
	//System.out.println("fin de wishedEmission3Treatment");
		
    }
    /**
     * 
     * @return the name of the third layer protocol used 
     */
    public String getProtocoleName(){
	
	return this.getTLProtocol().getProtocoleName();
    }
    
    /* (non-Javadoc)
     * @see Results.Dumpable#xmlDump()
     */
    public String xmlDump() {
	HashMap<String,Object> attr = new HashMap<String,Object>();
	attr.put("name",getProtocoleName());
	String s = XMLHelper.XMLCompleteTag("thirdlayerprotocol",attr);
	return s;
    }
    
    
    
    

}


